#include "scene.h"
#include <cmath>
#include <GL/gl.h>
#include <iostream>

#include "giftWrapping.h"
#include "advancingFront3D.h"

Scene::Scene(){
    idObjects = 0;
    idVertices = 0;
    selectedColor = Color(1.0, 0.0, 0.0, 0.6);
//    stepByStepColor = Color(1.0, 0.0, 0.0, 0.4);
//    stepByStepFace = 0;
//    stepByStepObject = 0;
}

Scene::~Scene(){
    Object *obj;
    unsigned int size = objects.size();
    for(unsigned int i = 0; i < size; i++){
        obj = objects.back();
        objects.pop_back();
        delete obj;
    }
}

void Scene::addObject(Object *_o){
    objects.push_back(_o);
}

void Scene::deleteObject(Object *_o){
    int size = selectedObjects.size();
    for (int i = size; i > 0; i--){
        if ( selectedObjects.at(i - 1) == _o ){
            selectedObjects.erase(selectedObjects.begin() + i - 1);
        }
    }

    size = selectedTetrahedrons.size();
    for (int i = size; i > 0; i--){
        if ( selectedTetrahedrons.at(i - 1)->getFace(0)->getObject() == _o ){
            selectedTetrahedrons.erase(selectedTetrahedrons.begin() + i - 1);
        }
    }

    size = selectedFaces.size();
    for (int i = size; i > 0; i--){
        if ( selectedFaces.at(i - 1)->getObject() == _o ){
            selectedFaces.erase(selectedFaces.begin() + i - 1);
        }
    }

    size = selectedEdges.size();
    Face *f;
    for (int i = size; i > 0; i--){
        f = selectedEdges.at(i - 1)->getClockwise();
        if(f){
            if( f->getObject() == _o ){
                selectedEdges.erase(selectedEdges.begin() + i - 1);
            }
        }else{
            if( selectedEdges.at(i - 1)->getCounterClockwise()->getObject() == _o ){
                selectedEdges.erase(selectedEdges.begin() + i - 1);
            }
        }
    }

//    size = selectedVertices.size();
//    for (int i = size; i > 0; i--){
////        std::cout << "pegando face" <<std::endl;
//        f = selectedVertices.at(i - 1)->getEdge()->getClockwise();
////        std::cout << "face pegada: " << f->getID() <<std::endl;
//        if(f){
////            std::cout << "Existe clockwise" <<std::endl;
//            if( f->getObject() == _o ){
//                selectedVertices.erase(selectedVertices.begin() + i - 1);
//            }
//        }else{
////            std::cout << "Existe? counter clockwise" <<std::endl;
//            if( selectedVertices.at(i - 1)->getEdge()->getCounterClockwise()->getObject() == _o ){
//                selectedVertices.erase(selectedVertices.begin() + i - 1);
//            }
//        }
//    }

    size = objects.size();
    for (int i = size; i > 0; i--){
        if ( objects.at(i - 1) == _o ){
            objects.erase(objects.begin() + i - 1);
        }
    }

    delete _o;
}

Object *Scene::getObject(int _i){
    return objects.at(_i);
}

std::vector<Object *>Scene::getObjects(){
    return objects;
}

std::vector<Object *>Scene::getSelectedObjects(){
    return selectedObjects;
}

int Scene::sizeObjects(){
    return objects.size();
}

int Scene::nextObjectsID(){
    return idObjects++;
}

int Scene::nextVerticesID(){
    return idVertices++;
}

void Scene::draw(bool _showVertices, bool _showEdges, bool _showWireframe, bool _showOpaque, bool _showNormal/*, bool _showStepByStep*/){
    Object *obj;
//    if(!_showStepByStep){
    for(unsigned int i = 0; i < objects.size(); i++){
        obj = objects.at(i);

        if(_showVertices){
            for(int j = 0; j < obj->sizeVertices(); j++){
                drawVertex(obj->getVertex(j));
            }
        }

        if(_showEdges){
            for(int j = 0; j < obj->sizeEdges(); j++){
                drawEdge(obj->getEdge(j));
            }
        }

        if(!_showWireframe){
            for(int j = 0; j < obj->sizeFaces(); j++){
                drawFace(obj->getFace(j), _showOpaque, _showNormal);
            }
        }else{
            for(int j = 0; j < obj->sizeTetrahedrons(); j++){
                drawTetrahedron(obj->getTetrahedron(j), _showOpaque, _showNormal);
            }
        }
    }
//    }else{
//        obj = objects.at(stepByStepObject);
//        if(!obj->isStepByStepFirstTime()){
//            for(unsigned int i = 0; i < objects.size(); i++){
//                obj = objects.at(i);
//
//                if(_showVertices){
//                    for(int j = 0; j < obj->sizeVertices(); j++){
//                        drawVertex(obj->getVertex(j));
//                    }
//                }
//
//                if(_showEdges){
//                    for(int j = 0; j < obj->sizeEdges(); j++){
//                        drawEdge(obj->getEdge(j));
//                    }
//                }
//
//                if(!_showWireframe){
//                    for(int j = 0; j < obj->sizeFaces(); j++){
//                        drawFace(obj->getFace(j), _showOpaque);
//                    }
//                }
//            }
//        }else{
//        }

//        unsigned int sizeObj = objects.size();
//        if(stepByStepObject >= sizeObj){
//            stepByStepObject = 0;
//        }else{
//
//        }


//        unsigned int sizeFace = objects.size()
//        obj->setStepByStepFirstTime(false);
//    }
}


void Scene::drawFace(Face *_f, bool _showOpaque, bool _showNormal, bool signal){
    if(!_showOpaque){
        if (_f->isSelected() || _f->getObject()->isSelected()) glColor4f(selectedColor.red, selectedColor.green, selectedColor.blue, selectedColor.alpha);
        else{
            Color c = _f->getColor();
            glColor4f(c.red, c.green, c.blue, c.alpha);
        }
    }else{
        if (_f->isSelected() || _f->getObject()->isSelected()) glColor3f(selectedColor.red, selectedColor.green, selectedColor.blue);
        else{
            Color c = _f->getColor();
            glColor3f(c.red, c.green, c.blue);
        }
    }

    Point p, normal = _f->normal(), center;
    if(!signal) normal = normal * (-1.0);
    int size = _f->getVerticesInThePlanSize();

    if(_f->isBoundary()){
        Edge *edge = _f->getEdge();
        bool edgeSignal = _f->getEdgeSignal();

        glBegin(GL_POLYGON);
            glNormal3dv(normal.intoVector());
            do{
                if(edgeSignal){
                    p = edge->getStart()->getPoint();
                    glVertex3dv( p.intoVector() );
                    edgeSignal = edge->getNextClockwiseSignal();
                    edge = edge->getNextClockwise();

                }else{
                    p = edge->getEnd()->getPoint();
                    glVertex3dv( p.intoVector() );
                    edgeSignal = edge->getNextCounterClockwiseSignal();
                    edge = edge->getNextCounterClockwise();
                }
            }while (edge != _f->getEdge());
        glEnd();
    }else{
        glBegin(GL_POLYGON);
            glNormal3dv(normal.intoVector());
            if(!signal){
                for(int i = size; i > 0; i--){
                    p = _f->getVertexInThePlan(i-1)->getPoint();
                    glVertex3dv( p.intoVector() );
                }
            }else{
                for(int i = 0; i < size; i++){
                    p = _f->getVertexInThePlan(i)->getPoint();
                    glVertex3dv( p.intoVector() );
                }
            }
        glEnd();
    }
    for(int i = 0; i < size; i++){
        center = center + _f->getVertexInThePlan(i)->getPoint();
    }
    center = center * (1.0/size);

    if(_showNormal){
        glDisable(GL_LIGHT0);
        glDisable(GL_LIGHTING);
        if(!signal) glColor3f(1., 0., 0.);
        else glColor3f(0., 1., 0.);
        glLineWidth(2);
        glBegin(GL_LINE);
            glVertex3dv( center.intoVector() );
            glVertex3dv( (center + (normal*0.5)).intoVector() );
        glEnd();
        glLineWidth(1);
        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHTING);
    }
}

void Scene::drawEdge(Edge *e){
    glDisable(GL_LIGHT0);
    glDisable(GL_LIGHTING);

    if (e->isSelected()){
        glColor3f(selectedColor.red, selectedColor.green, selectedColor.blue);
        glLineWidth(2);
    }
    else glColor3f(0.8, 0.8, 0.8);

    glBegin(GL_LINES);
        glVertex3dv( e->getStart()->getPoint().intoVector() );
        glVertex3dv( e->getEnd()->getPoint().intoVector() );
    glEnd();
    glLineWidth(1);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
}

void Scene::drawVertex(Vertex *_v){
    glDisable(GL_LIGHT0);
    glDisable(GL_LIGHTING);

    if (_v->isSelected()){
        glColor3f(selectedColor.red, selectedColor.green, selectedColor.blue);
        glPointSize(3);
    }
    else glColor3f(1, 1, 1);

    glBegin(GL_POINTS);
        glVertex3dv( _v->getPoint().intoVector() );
    glEnd();
    glPointSize(1);

    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
}

void Scene::drawTetrahedron(Tetrahedron *_t, bool _showOpaque, bool _showNormal){
    if(_t->isSelected()){
        glColor3f(selectedColor.red, selectedColor.green, selectedColor.blue);
        for(int i = 0; i < _t->facesSize(); i++){
            drawFace(_t->getFace(i), _showOpaque, _showNormal, _t->getFaceSignal(i));
        }
    }
}

void Scene::selectTetrahedron(Tetrahedron *_t){
    if(_t->isSelected()){
        selectedTetrahedrons.push_back(_t);
    }else{
        int size = selectedTetrahedrons.size();
        for (int i = size; i > 0; i--){
            if ( selectedTetrahedrons.at(i - 1) == _t ){
                selectedTetrahedrons.erase(selectedTetrahedrons.begin() + i - 1);
                return;
            }
        }
    }
}

void Scene::selectVertex(Vertex *_v){
    if(_v->isSelected()){
        selectedVertices.push_back(_v);
    }else{
        int size = selectedVertices.size();
        for (int i = size; i > 0; i--){
            if ( selectedVertices.at(i - 1) == _v ){
                selectedVertices.erase(selectedVertices.begin() + i - 1);
                return;
            }
        }
    }
}

void Scene::selectEdge(Edge *_e){
    if(_e->isSelected()){
        selectedEdges.push_back(_e);
    }else{
        int size = selectedEdges.size();
        for (int i = size; i > 0; i--){
            if ( selectedEdges.at(i - 1) == _e ){
                selectedEdges.erase(selectedEdges.begin() + i - 1);
                return;
            }
        }
    }
}

void Scene::selectFace(Face *_f){
    if(_f->isSelected()){
        selectedFaces.push_back(_f);
    }else{
        int size = selectedFaces.size();
        for (int i = size; i > 0; i--){
            if ( selectedFaces.at(i - 1) == _f ){
                selectedFaces.erase(selectedFaces.begin() + i - 1);
                return;
            }
        }
    }
}

void Scene::selectObject(Object *_o){
    if(_o->isSelected()){
        selectedObjects.push_back(_o);
    }else{
        int size = selectedObjects.size();
        for (int i = size; i > 0; i--){
            if ( selectedObjects.at(i - 1) == _o ){
                selectedObjects.erase(selectedObjects.begin() + i - 1);
                return;
            }
        }
    }
}

std::vector<Vertex *> Scene::selectConvexHullVertices(){
    std::vector<Vertex *> result;
    std::vector<Vertex *> resultObject;
    for(unsigned int i = 0; i < selectedObjects.size(); i++){
        resultObject = selectedObjects.at(i)->selectConvexHullVertices();
        result.insert(result.end(), resultObject.begin(), resultObject.end());
    }
    return result;
}

void Scene::convexHull(){
    //***aqui é pra ser nos objetos selecionados
    Object *obj;
    for(unsigned int i = 0; i < objects.size(); i++){
        obj = objects.at(i);
        if(obj->sizeVertices() <= 2) continue;
        if(!obj->haveConvexHull()){
            GiftWrapping giftWrapping;
            giftWrapping.calculate(obj);
            obj->setConvexHull(true);
//            obj->setStepByStepFirstTime(true);
        }
    }
}

void Scene::tetrahedralization(){
    //***aqui é pra ser nos objetos selecionados
    Object *obj;
    for(unsigned int i = 0; i < objects.size(); i++){
        obj = objects.at(i);
        if(obj->sizeVertices() <= 3) continue;
        if(!obj->haveConvexHull()){//faz o convexhull caso ele não tenha
            GiftWrapping giftWrapping;
            giftWrapping.calculate(obj);
            obj->setConvexHull(true);
        }
        if(!obj->haveTetrahedrons()){
            AdvancingFront3D advancingFront;
            advancingFront.calculate(obj);
            obj->setTetrahedrons(true);
//            obj->setStepByStepFirstTime(true);
        }
    }
}
